package com.katze.boot.applet.ssh.service.impl;

import com.katze.boot.applet.ssh.dao.SshProcessMapper;
import com.katze.boot.applet.ssh.dao.SshSessionMapper;
import com.katze.boot.applet.ssh.entity.SshProcessEntity;
import com.katze.boot.applet.ssh.entity.SshSessionEntity;
import com.katze.boot.applet.ssh.service.SecureShellService;
import com.katze.ssh.SshSessionKey;
import com.katze.ssh.SshSessionPool;
import com.katze.ssh.cmd.ExecChannel;
import com.katze.ssh.cmd.ShellChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;

@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class SecureShellServiceImpl  implements SecureShellService {

    @Autowired
    private SshSessionMapper sshSessionMapper;

    @Autowired
    private SshProcessMapper sshProcessMapper;

    @Autowired
    private SshSessionPool sessionPool;

    @Override
    public SshSessionKey getSessionProperties(String sessionId) {
        return sessionPool.getSessionConfig(sessionId, this::findSessionById);
    }

    @Override
    public List<SshSessionEntity> findSessions(SshSessionEntity entity) {
        if (entity.getHost() != null && entity.getHost().contains(":")){
            entity.setHost(null);
        }
        return sshSessionMapper.findSessions(entity);
    }

    @Override
    public SshSessionEntity findSessionById(Serializable id) {
        return sshSessionMapper.selectById(id);
    }

    @Override
    public List<SshProcessEntity> findProcessBySession (String sessionId) throws Exception {
        List<SshProcessEntity> entities = sshProcessMapper.findProcessBySession(sessionId);
        if (entities.isEmpty()) {
            return entities;
        }
        ExecChannel channel = sessionPool.borrow(getSessionProperties(sessionId), ExecChannel.class);
        channel.psUO(item -> {
            for (SshProcessEntity entity : entities) {
                String cmd = item.getProperty("cmd");
                String[] items = entity.getName().split(";");
                boolean rs = true;
                for (String str : items) {
                    rs &= cmd.contains(str);
                }
                if (rs){
                    entity.setPid(entity.getPid() == null? item.getProperty("pid"): entity.getPid() + ";" + item.getProperty("pid"));
                }
            }
        });
        // 更新实际进程PID信息
        sshProcessMapper.batchUpdatePid(entities);
        return entities;
    }

    @Override
    public Collection<String> findProcessName(String owner, String name) {
        List<String> list = sshProcessMapper.findProcessNameByOwner(owner, name);
        String value = String.join(";", list);
        Set<String> names = new HashSet<>();
        String[] items = value.split(";");
        for (String str : items) {
            if (str.contains(name)){
                names.add(str);
            }
        }
        return names;
    }

    @Override
    @Transactional
    public boolean saveOrUpdate(SshSessionEntity entity) {
        return sshSessionMapper.insertOrUpdate(entity);
    }

    @Override
    @Transactional
    public boolean saveOrUpdate(SshProcessEntity entity) {
        return sshProcessMapper.insertOrUpdate(entity);
    }

    @Override
    @Transactional
    public boolean deleteSessionById(Serializable id) {
        return sshSessionMapper.deleteById(id) > 0;
    }

    @Override
    @Transactional
    public boolean deleteProcessById(Serializable id) {
        return sshProcessMapper.deleteById(id) > 0;
    }

    @Override
    public List<Map<String, String>> execute(String cmd, String sessionId, String[] processes) throws Exception {
        ExecChannel channel = sessionPool.borrow(getSessionProperties(sessionId), ExecChannel.class);
        List<SshProcessEntity> entities = sshProcessMapper.findProcessById(Set.of(processes));
        List<Map<String, String>> rs = new ArrayList<>();
        for (SshProcessEntity entity : entities) {
            Map<String, String> msg = new HashMap<>();
            msg.put("title", entity.getTitle());
            switch (cmd) {
                case "startup":
                    channel.script(entity.getPosition(), entity.getStartup(), item -> msg.merge("description", item, (v1, v2)-> v1 + "\r\n" + v2));
                    break;
                case "shutdown":
                    if (entity.getShutdown() != null && !entity.getShutdown().isBlank()) {
                        channel.script(entity.getPosition(), entity.getShutdown(), item -> msg.merge("description", item, (v1, v2)-> v1 + "\r\n" + v2));
                    } else {
                        channel.kill(entity.getPid(), item -> msg.put("description", "进程正在关闭..."));
                    }
                    break;
            }
            rs.add(msg);
        }
        return rs;
    }

    @Override
    public ShellChannel createShell(String sessionId) throws Exception {
        return sessionPool.borrow(getSessionProperties(sessionId), ShellChannel.class);
    }

    @Override
    public void exit(String sessionId) {
        sessionPool.clear(sessionId);
    }
}
